<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // 1、原型链继承---缺点是原型属性会被所有的实例共享，换言之一个改变，其他的都改变，也为后面的构造函数继承的出现，埋下了伏笔
        function Person(name){
            this.name = name
        }
        Person.prototype.sayHello = function(){
            console.log('你好,my name is' + this.name);
        }
        var person1 = new Person('Alice');
        var person2 = new Person('Bob');

        person1.sayHello()
        person2.sayHello()

        // 2、构造函数apply,call,bind 继承，能继承父类构造函数中的属性和方法,缺点是不能继承父类prototype上的属性,也就为后面的组合继承(原型链+构造函数继承)埋下伏笔
            // 父类
        function Person2(){
            this.sayHello = function(){
                console.log('父类Hello');
            }
        }
        Person2.prototype.a = '我是父类prototype上的属性'
            // 子类
            function Child(){
                Person2.call(this) // 内存地址不一样 所以下面的是false
            }

            // 创建两个 Child 实例
            let child1 = new Child();
            let child2 = new Child();
            console.log(child1.sayHello === child2.sayHello); // false

            // 缺点演示
            console.log(new Person2().a); // 我是父类prototype上的属性
            console.log(child1.a); // undefined
            console.log(child1.sayHello());  // 父类hello---能继承父类构造函数中的属性和方法
            
            
        // 3、组合继承
            // 父类
        function Parent(){
            this.sayHello = function (){
                console.log('Hello Parent');
            }
        }
        Parent.prototype.a = '我是父类prototype上的属性'
            // 子类
            function Child(){
                Parent.call(this)
            }
            Child.prototype = new Parent(); // 这是关键代码

            var child3 = new Child();
            console.log('child3.a',child3.a); // '我是父类prototype上的属性'
            
            // 优缺点
                // 优： 原型属性不会被共享，可以继承父类原型链上的属性和方法
                // 缺: 调用了 2次 Parent


        // 4、寄生组合继承--(最佳，但是实现有点小复杂)
        var proto = {
            sayHello: function(){
                console.log('Hello');
            }
        }
        var obj = Object.create(proto) // Object.crate是创建一个空对象，并且指定这个空对象的原型是谁，--这里指向proto
        obj.sayHello()

        // 父类
        function Parent2(){
            this.sayHello = function(){
                console.log('Hello');
            }
        }
        Parent2.prototype.a = '我是父类prototype上的属性'
        // 子类
        function Child(){
            Parent2.call(this)
        }
        
        // 创建一个么有实例方法的父类作为子类的原型
        Child.prototype = Object.create(Parent2.prototype)

        Child.prototype.childFunction = ()=>{ // 这一行代码如果是写在 Child.prototype = Object.create(Parent2.prototype)的上面会被清空这个方法，这也是寄生组合继承的缺点，所以要写在下面
            console.log('我是child方法');
        }
        // 修复构造函数的指向
        Child.prototype.constructor = Child
        var child3 = new Child()
        child3.childFunction()
    </script>
</body>
</html>